package com.loser.module.base.proto.base;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.loser.module.base.mq.gateway.CmdRegisterDTO;
import com.loser.module.base.mq.gateway.GateWayConst;
import com.loser.module.base.proto.CmdBeanUtils;
import com.loser.module.base.proto.sdk.cmd.CmdMethod;
import com.loser.utils.ContextUtil;
import com.loser.utils.SerializationUtil;
import com.loser.utils.trace.MDCUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;

@SuppressWarnings("all")
public interface CmdFeign {

    static final Logger log = LoggerFactory.getLogger(CmdFeign.class);

    @PostMapping("/cmd/invoke")
    default InnerResponse cmdInvoke(@RequestBody InnerRequest req) throws Exception {

        try {
            long start = System.currentTimeMillis();
            MDCUtil.setTraceId(req.getTraceId());
            Class<? extends LiveBaseReq> invokeClass = (Class<? extends LiveBaseReq>) Class.forName(req.getArgClass());
            LiveBaseReq methodReq = SerializationUtil.deserializeFromByte(req.getBytes(), invokeClass);
            Class<? extends LiveCmdHandler> handlerClass = (Class<? extends LiveCmdHandler>) Class.forName(req.getHandlerClass());
            LiveCmdHandler handler = ContextUtil.getBean(handlerClass);
            Method invokeMethod = handler.getClass().getMethod(req.getMethod(), invokeClass);
            Object invoke = invokeMethod.invoke(handler, methodReq);
            LiveBaseResult result = (LiveBaseResult) invoke;
            InnerResponse response = new InnerResponse();
            response.setTypeCmd(CmdBeanUtils.getByClass(result.getClass()));
            response.setBytes(SerializationUtil.serializeToByte(result));
            long end = System.currentTimeMillis();
            log.info("cmdInvoke cost {} method {} req {} resp {}", end - start, req.getMethod(), JSONObject.toJSONString(methodReq), JSONObject.toJSONString(result));
            return response;
        } finally {
            MDCUtil.removeTraceId();
        }

    }


    /**
     * 注册cmd接口
     */
    public static void registerCmd() {

        List<LiveCmdHandler> beans = ContextUtil.getBeans(LiveCmdHandler.class);
        if (CollectionUtil.isNotEmpty(beans)) {
            RabbitTemplate rabbitTemplate = ContextUtil.getBean(RabbitTemplate.class);
            for (LiveCmdHandler cmdHandler : beans) {
                regiestByHandler(rabbitTemplate, cmdHandler);
            }
        }

    }

    public static void regiestByHandler(RabbitTemplate rabbitTemplate, LiveCmdHandler cmdHandler) {

        Type[] genericInterfaces = cmdHandler.getClass().getGenericInterfaces();

        for (Type genericInterface : genericInterfaces) {
            Class<?> cmdClass = (Class<?>) genericInterface;
            Method[] methods = cmdClass.getMethods();
            for (Method method : methods) {
                registerMethod(rabbitTemplate, cmdHandler, method, cmdClass);
            }

        }
    }

    public static void registerMethod(RabbitTemplate rabbitTemplate, LiveCmdHandler cmdHandler, Method method, Class<?> cmdClass) {

        CmdMethod cmdMethod = method.getAnnotation(CmdMethod.class);
        if (Objects.isNull(cmdMethod)) {
            return;
        }
        int cmd = cmdMethod.cmd();
        String service = cmdMethod.service();
        String methodName = method.getName();
        String argClass = method.getParameterTypes()[0].getName();
        CmdRegisterDTO dto = new CmdRegisterDTO();
        dto.setCmd(cmd);
        dto.setServiceName(service);
        dto.setHandlerClass(cmdClass.getName());
        dto.setMethod(methodName);
        dto.setArgClass(argClass);
        rabbitTemplate.convertAndSend(GateWayConst.CmdRegister.exchange, GateWayConst.CmdRegister.routeKey, dto);
        log.info("register cmd data {}", JSONObject.toJSONString(dto));

    }

}
